package jp.co.nes.awssupport.common.utility;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.ResolverStyle;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import jp.co.nes.awssupport.common.base.CommonException;

/**
 * 日時関連ユーティリティです。
 */
public class DateUtil {

    /**
     * デフォルトコンストラクタです。<br>
     * 本クラスはインスタンスを生成しません。
     */
    private DateUtil() {
    }

    /**
     * Timestamp型をZonedDateTime型に変換して、返却します。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static ZonedDateTime toZonedDateTime(Timestamp value) {
        return toZonedDateTime(new Date(value.getTime() + (value.getNanos() / 1000000)));
    }

    /**
     * Date型をZonedDateTime型に変換して、返却します(タイムゾーンはUTC)。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static ZonedDateTime toZonedDateTime(Date value) {
        return ZonedDateTime.ofInstant(value.toInstant(), ZoneId.of("UTC"));
    }

    /**
     * 引数で指定された内容でZonedDateTime型を生成し、返却します(タイムゾーンはUTC)。
     *
     * @param year 年
     * @param month 月
     * @param day 日
     * @return 生成された値
     */
    public static ZonedDateTime toZonedDateTime(int year, int month, int day) {
        return toZonedDateTime(year, month, day, 0, 0, 0);
    }

    /**
     * 引数で指定された内容でZonedDateTime型を生成し、返却します(タイムゾーンはUTC)。
     *
     * @param year 年
     * @param month 月
     * @param day 日
     * @param hour 時
     * @param minute 分
     * @param second 秒
     * @return 生成された値
     */
    public static ZonedDateTime toZonedDateTime(int year, int month, int day, int hour, int minute, int second) {
        return ZonedDateTime.of(year, month, day, hour, minute, second, 0, ZoneId.of("UTC"));
    }

    /**
     * 引数で指定された内容でZonedDateTime型を生成し、返却します(タイムゾーンはUTC)。
     *
     * @param year 年
     * @param month 月
     * @param day 日
     * @param hour 時
     * @param minute 分
     * @param second 秒
     * @param nanoOfSecond ナノ秒(0～999999999)
     * @return 生成された値
     */
    public static ZonedDateTime toZonedDateTime(int year, int month, int day, int hour, int minute, int second,
            int nanoOfSecond) {
        return ZonedDateTime.of(year, month, day, hour, minute, second, nanoOfSecond, ZoneId.of("UTC"));
    }

    /**
     * 引数で指定された内容(uuuu-MM-dd HH:mm:ss形式)でZonedDateTime型を生成し、返却します(タイムゾーンはUTC)。
     *
     * @param value 値
     * @return 生成された値
     */
    public static ZonedDateTime toZonedDateTime(String value) {
        return toZonedDateTime(value, "uuuu-MM-dd HH:mm:ss", "UTC");
    }

    /**
     * 引数で指定された内容(uuuu-MM-dd形式)でZonedDateTime型を生成し、返却します(タイムゾーンはUTC)。<br>
     * なお、時分秒は0になります。
     *
     * @param value 値
     * @return 生成された値
     */
    public static ZonedDateTime toZonedDateTimeFromuuuuMMdd(String value) {
        return toZonedDateTime(value + " 00:00:00", "uuuu-MM-dd HH:mm:ss", "UTC");
    }

    /**
     * 引数で指定された内容でZonedDateTime型を生成し、返却します。
     *
     * @param value 値
     * @param pattern パターン
     * @param zoneid ゾーンID
     * @return 生成された値
     */
    public static ZonedDateTime toZonedDateTime(String value, String pattern, String zoneid) {
        LocalDateTime localDateTime = LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern)
                .withResolverStyle(ResolverStyle.STRICT));
        return localDateTime.atZone(ZoneId.of(zoneid, ZoneId.SHORT_IDS));
    }

    /**
     * Date型をYearMonth型に変換して、返却します(タイムゾーンはUTC)。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static YearMonth toYearMonth(Date value) {
        Calendar calendar = getUTCCalendar();
        calendar.clear();
        calendar.setTime(value);
        int year = calendar.get(Calendar.YEAR);
        int month = calendar.get(Calendar.MONTH);
        return YearMonth.of(year, month + 1);
    }

    /**
     * 引数で指定された内容でYearMonth型を生成し、返却します。
     *
     * @param year 年
     * @param month 月
     * @return 生成された値
     */
    public static YearMonth toYearMonth(int year, int month) {
        return YearMonth.of(year, month);
    }

    /**
     * 引数で指定された内容(uuuuMM形式)でYearMonth型を生成し、返却します。
     *
     * @param value 値
     * @return 生成された値
     */
    public static YearMonth toYearMonth(String value) {
        return YearMonth.parse(value, DateTimeFormatter.ofPattern("uuuuMM"));
    }

    public static YearMonth toYearMonth(ZonedDateTime value) {
        return toYearMonth(toString(value, "uuuuMM"));
    }

    /**
     * ZonedDateTime型をString型に変換して、返却します。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static String toString(ZonedDateTime value) {
        return toString(value, "uuuuMMddHHmmss");
    }

    /**
     * 引数で指定された内容でString型を生成し、返却します。
     *
     * @param value 変換前の値
     * @param pattern パターン
     * @return 変換後の値
     */
    public static String toString(ZonedDateTime value, String pattern) {
        return value.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * YearMonth型をString型(uuuuMM形式)に変換して、返却します。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static String toString(YearMonth value) {
        return value.format(DateTimeFormatter.ofPattern("uuuuMM"));
    }

    /**
     * 引数で指定された内容でString型(uuuuMM形式)を生成し、返却します。
     *
     * @param year 年
     * @param month 月
     * @return 生成された値
     */
    public static String toString(int year, int month) {
        return toString(toYearMonth(year, month));
    }

    /**
     * YearMonth型をint型に変換して、返却します。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static int toInt(YearMonth value) {
        return Integer.parseInt(toString(value));
    }

    /**
     * ZonedDateTime型をTimestamp型に変換して、返却します。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static Timestamp toTimestamp(ZonedDateTime value) {
        return Timestamp.from(value.toInstant());
    }

    /**
     * ZonedDateTime型をDate型に変換して、返却します。
     *
     * @param value 変換前の値
     * @return 変換後の値
     */
    public static Date toDate(ZonedDateTime value) {
        return Date.from(value.toInstant());
    }

    /**
     * 引数で指定された内容(yyyy-MM-dd HH:mm:ss形式)でDate型を生成し、返却します。
     *
     * @param value 値
     * @return 生成された値
     */
    public static Date toDate(String value) {
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        df.setLenient(false);
        try {
            return df.parse(value);
        } catch (ParseException e) {
            throw new CommonException(e);
        }
    }

    /**
     * ZonedDateTime型のシステム日時を返却します(タイムゾーンはUTC)。
     *
     * @return システム日時を返却します(タイムゾーンはUTC)
     */
    public static ZonedDateTime getNowOfZonedDateTime() {
        return ZonedDateTime.now(ZoneId.of("UTC"));
    }

    /**
     * YearMonth型のシステム年月を返却します(タイムゾーンはUTC)。
     *
     * @return システム年月を返却します(タイムゾーンはUTC)
     */
    public static YearMonth getNowOfYearMonth() {
        return YearMonth.now(ZoneId.of("UTC"));
    }

    /**
     * タイムゾーン=UTCを設定したCalendarオブジェクトを返却します。<br>
     * 返却されたオブジェクトは、JDBCのResultSetのgetTimestamp()を使って
     * 値を取得する際に、第2引数に設定するCalendarオブジェクトとして使用します。
     *
     * @return タイムゾーン=UTCを設定したCalendarオブジェクト
     */
    public static Calendar getUTCCalendarForResultSet() {
        return getUTCCalendar();
    }

    /**
     * 文字列から、指定の書式へ変換したTimestampを返却します。
     *
     * @param src    値
     * @param format 日付書式
     * @return Timestampオブジェクト
     */
    public static Timestamp parseTimeStamp(String src, String format) {
        try {
            return new Timestamp(new SimpleDateFormat(format).parse(src).getTime());
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * タイムゾーン=UTCを設定したCalendarオブジェクトを返却します。
     *
     * @return タイムゾーン=UTCを設定したCalendarオブジェクト
     */
    private static Calendar getUTCCalendar() {
        TimeZone timeZone = TimeZone.getTimeZone("UTC");
        Calendar calendar = Calendar.getInstance(timeZone);
        return calendar;
    }
}
